// Copyright (C) 2010 Razvan Constantin <razvan.constantin@rconst.net>
//
// This file is part of FileBow.
//
// FileBow is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// FileBow is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with FileBow.  If not, see <http://www.gnu.org/licenses/>.
//
// Implementation of FileFilter.

#include "logic/file_filter.h"

#include "common/strings.h"

using namespace std;

namespace filebow {

FilterResult CombineFilterResult(FilterResult res1, FilterResult res2) {
  if (res1 == EXCLUDE || res2 == EXCLUDE) {
    return EXCLUDE;
  } else if (res1 == INCLUDE || res2 == INCLUDE) {
    return INCLUDE;
  } else {
    return DONT_CARE;
  }
}

FileFilter::FileFilter() {}

FileFilter::~FileFilter() {}

FileFilter* FileFilter::FilterPartial(const PathTree::Node& node) const {
  return NULL;
}

FilterResult FileFilter::Filter(const PathTree::Node& node) const {
  return DONT_CARE;
}

CompositeFilter::CompositeFilter() {}

CompositeFilter::~CompositeFilter() {
  STLDeleteAll(&mFilters);
}

void CompositeFilter::AddFilter(FileFilter* filter) {
  mFilters.push_back(filter);
}

FileFilter* CompositeFilter::FilterPartial(const PathTree::Node& node) const {
  vector<FileFilter*> partial_filters;
  for (int i = 0; i < mFilters.size(); ++i) {
    FileFilter* partial_filter = mFilters[i]->FilterPartial(node);
    if (partial_filter) {
      partial_filters.push_back(partial_filter);
    }
  }
  if (!partial_filters.size()) {
    return NULL;
  }
  CompositeFilter* partial_filter = new CompositeFilter();
  partial_filter->mFilters.swap(partial_filters);
  return partial_filter;
}

FilterResult CompositeFilter::Filter(const PathTree::Node& node) const {
  FilterResult result = DONT_CARE;
  for (int i = 0; result != EXCLUDE && i < mFilters.size(); ++i) {
    result = CombineFilterResult(result, mFilters[i]->Filter(node));
  }
  return result;
}

ConstFilter::ConstFilter(FilterResult result) : mResult(result) {}

ConstFilter::~ConstFilter() {}

FilterResult ConstFilter::Filter(const PathTree::Node& node) const {
  return mResult;
}

MatchFilter::MatchFilter() {}

MatchFilter::~MatchFilter() {}

FileFilter* MatchFilter::FilterPartial(const PathTree::Node& node) const {
  vector<string> remaining;
  for (int i = 0; i < mFragments.size(); ++i) {
    if (node.name().find(mFragments[i]) == string::npos) {
      remaining.push_back(mFragments[i]);
    }
  }
  if (!remaining.size()) {
    return new ConstFilter(INCLUDE);
  }
  MatchFilter* partialFilter = new MatchFilter();
  partialFilter->mFragments.swap(remaining);
  return partialFilter;
}

FilterResult MatchFilter::Filter(const PathTree::Node& node) const {
  if (!IsFile(node)) {
    return DONT_CARE;
  }
  for (int i = 0; i < mFragments.size(); ++i) {
    if (node.name().find(mFragments[i]) == string::npos) {
      return EXCLUDE;
    }
  }
  return INCLUDE;
}

ExcludeFilter::ExcludeFilter() {}

ExcludeFilter::~ExcludeFilter() {}

FilterResult ExcludeFilter::Filter(const PathTree::Node& node) const {
  for (int i = 0; i < mFragments.size(); ++i) {
    if (node.name().find(mFragments[i]) == string::npos) {
      return EXCLUDE;
    }
  }
  return DONT_CARE;
}

FileFilter* EnsurePartialFilter(const FileFilter& filter,
                                const PathTree::Node& node) {
  FileFilter* partialFilter = filter.FilterPartial(node);
  if (!partialFilter) {
    partialFilter = new ConstFilter(filter.Filter(node));
  }
  return partialFilter;
}

FileFilter* ParseFilter(const string& filterText) {
  vector<string> parts;
  Split(filterText, ' ', &parts);
  MatchFilter* matchFilter = NULL;
  ExcludeFilter* excludeFilter = NULL;
  for (int i=0; i<parts.size(); ++i) {
    const string& w = parts[i];
    if (w.empty() || w == "-") {
      continue;
    }
    if (w[0] == '-') {
      excludeFilter = excludeFilter ? excludeFilter : new ExcludeFilter;
      excludeFilter->fragments()->push_back(w.substr(1));
    } else {
      matchFilter = matchFilter ? matchFilter : new MatchFilter;
      matchFilter->fragments()->push_back(w);
    }
  }
  if (matchFilter && excludeFilter) {
    CompositeFilter* filter = new CompositeFilter();
    filter->AddFilter(matchFilter);
    filter->AddFilter(excludeFilter);
    return filter;
  } else if (matchFilter) {
    return matchFilter;
  } else if (excludeFilter) {
    return excludeFilter;
  } else {
    return new ConstFilter(DONT_CARE);
  }
}

} // namespace filebow
